#ifndef MDS_OPTIONS_INCLUDED
#define MDS_OPTIONS_INCLUDED 1

#include "../sys/list.h"
/*============================================================================
 * 
 * Basic command line options parsing
 *
 *============================================================================*/

/** \brief An option as a doubly linked node 
 */
typedef struct mds_option_
{
	struct mds_option_ *next;
	struct mds_option_ *prev;
	char               *name;
	char               *value;
} mds_option;

mds_option *mds_option_create( const char *name, const char *value );  
void        mds_option_delete( mds_option *option );

/** \brief A list of options */
typedef struct
{
	mds_option *head;
	mds_option *tail;
	size_t      size;
} mds_options;

/** \brief Static options initializer */
#define MDS_OPTIONS_INIT MDS_LIST_INIT

/** \brief Dynamic options initializer. */
void mds_options_init( mds_options *options );

/** \brief Delete all options. */
void mds_options_quit( mds_options *options );

/** \brief Parse options from command line.
 */
int  mds_options_parse( mds_options *options, int argc, char **argv );

/** \brief load from a file
	\param options options list
	\param filename file to parse
	\param after if after != NULL then the file options are appended after the
 first option named "after". Otherwise, the file options are added to the end of options.
 */
int mds_options_load( mds_options *options, const char *filename, const char *after );

/*============================================================================
 * 
 * Advanced command line options parsing.
 *
 *============================================================================*/
typedef int (*mds_option_proc)( const char *value, void *args );

#define MDS_OPTION_HOOK_TEXT_SIZE 64
#define MDS_OPTION_HOOK_NAME_SIZE (MDS_OPTION_HOOK_TEXT_SIZE-(sizeof(mds_option_proc)+sizeof(void*)))
#define MDS_OPTION_HOOK_INFO_SIZE (MDS_OPTION_HOOK_TEXT_SIZE-(sizeof(int)))
typedef struct 
{
	mds_option_proc proc;                            /*!< what to do with option->value    */
	void           *args;                            /*!< proc(option->value,args)         */
	int             forced;                          /*!< not zero if mandatory            */
	const char      name[MDS_OPTION_HOOK_NAME_SIZE]; /*!< option name : "np" for "-np" ... */
	const char      info[MDS_OPTION_HOOK_TEXT_SIZE]; /*!< helper for usage display         */
} mds_option_hook;



#define MDS_OPTIONS_DECL()  mds_option_hook __mds_option_hooks[] = 
#define MDS_OPTION_NORMAL(ID,DATA,INFO)   { on_option_##ID, &(DATA), 0, #ID, INFO }
#define MDS_OPTION_FORCED(ID,DATA,INFO)   { on_option_##ID, &(DATA), 1, #ID, INFO } 
#define MDS_OPTIONS_DONE()  ; const size_t __mds_num_option_hooks = sizeof(__mds_option_hooks) / sizeof( __mds_option_hooks[0] )

void    mds_options_help( FILE *fp, const mds_option_hook *reg, const size_t num );
#define MDS_OPTIONS_HELP(FP) mds_options_help(FP, __mds_option_hooks, __mds_num_option_hooks)

#define MDS_OPTIONS_DISPATCH(OPTIONS,ON_FAILURE) do {                                                  \
/*	*/	mds_option * __mds_option = OPTIONS.head;                                                      \
/*	*/	register size_t __mds_hook_index;                                                              \
/*	*/	for(;__mds_option != NULL; __mds_option = __mds_option->next )                                 \
/*	*/	{                                                                                              \
/*	*/		for( __mds_hook_index = 0; __mds_hook_index < __mds_num_option_hooks; ++__mds_hook_index ) \
/*	*/		{                                                                                          \
/*	*/			mds_option_hook *__mds_hook = & __mds_option_hooks[ __mds_hook_index ];                \
/*	*/			if( 0 == strcmp( __mds_hook->name, __mds_option->name ) )                              \
/*	*/			{                                                                                      \
/*	*/				if( __mds_hook->proc( __mds_option->value, __mds_hook->args ) < 0 )                \
/*	*/				{ ON_FAILURE; }                                                                    \
/*	*/				__mds_hook->forced = 0;                                                            \
/*	*/				break;                                                                             \
/*	*/			}                                                                                      \
/*	*/		}                                                                                          \
/*	*/	} \
/*	*/	for( __mds_hook_index = 0; __mds_hook_index < __mds_num_option_hooks; ++__mds_hook_index )     \
/*	*/	{                                                                                              \
/*	*/		mds_option_hook *__mds_hook = & __mds_option_hooks[ __mds_hook_index ];                    \
/*	*/		if( __mds_hook->forced ) {                                                                 \
/*	*/			fprintf(stderr, "\tmissing option '-%s' (%s)\n", __mds_hook->name, __mds_hook->info ); \
/*	*/			ON_FAILURE; }                                                                          \
/*	*/	} \
/*	*/	} while( 0 )


void mds_options_display( const mds_options *options, FILE *fp ); 


#endif
